﻿using System;
using System.Collections.Generic;
using System.IO;
using ICSharpCode.SharpZipLib.Zip;

namespace bobolib.zip.compress
{
    public class CompressHelper
    {
        public string getTempFold()
        {
            String sPath = Path.GetTempPath();

            Random o = new Random();
            String tempR;
            while (true)
            {
                int i=o.Next(1000);
                tempR = sPath + "CSZip_" + i;
                if (File.Exists(tempR))
                    continue;
                break;
            }
            try
            {
                Console.WriteLine("Temporary Path := " + tempR);
                Directory.CreateDirectory(tempR);
                return tempR;
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
            return null;
        }
        protected String seekCommonPath(String path1,String path2)
        {
            int l1=path1.Length;
            int l2 = path2.Length;
            int ll = l1 < l2 ? l1 : l2;
            int lastpos = -1;
            for (int i = 0; i < ll; i++)
            {
                if (path1[i] != path1[i])
                    break;
                if (path1[i].Equals('\\'))
                {
                    lastpos = i;
                }
            }
            if(lastpos>0)
            {
                return path1.Substring(0, lastpos + 1);
            }
            return null;
        }
        protected String seekCommonPath(List<String> fileNames)
        {
                         //1.找出每一個root ,最短的
            int minlen=int.MaxValue;;
            String rootdir = "";
            foreach(String s in fileNames)
            {
                String r=Path.GetFullPath(s);
                if (r.Length < minlen)
                {
                    if (rootdir.Equals(""))
                    {
                        rootdir = r;
                        minlen = rootdir.Length;
                        continue;
                    }
                    if (rootdir.IndexOf(r) < 0)
                    {
                        //SeekCommon.
                        String result=seekCommonPath(rootdir, r);
                        if (result == null)
                        {
                            return null;
                        }
                        else
                        {
                            rootdir = result;
                            minlen = rootdir.Length;
                        }
                    }
                    else
                    {
                        rootdir = r;
                        minlen = r.Length;
                    }
                }
            }
            //找到root了...
            //fetch each segment
            System.Diagnostics.Debug.WriteLine(rootdir);
            
            return rootdir;
        }
        protected void moveFileTo(String src, String basepath, String targetpath)
        {
            String newdest = src.Substring(basepath.Length);
            if (targetpath.EndsWith("\\") == false)
                targetpath = targetpath + "\\";
            newdest = targetpath + newdest;
            System.Diagnostics.Debug.WriteLine("Move " + src + " to " + newdest);
            Directory.CreateDirectory(Path.GetDirectoryName(newdest));
            File.Copy(src, newdest);
        }
        protected List<String> convertToFileList(List<String> fileNames)
        {
            
            HashSet<String> rr = new HashSet<string>();
            foreach (String s in fileNames)
            {
                string[] files=Directory.GetFiles(s, "*.*", SearchOption.AllDirectories);
                foreach (String f in files)
                {
                    rr.Add(f);
                }
            }
            List<String> result = new List<string>();
            result.AddRange(rr);
            return result;
        }
        public int compressFiles(List<String> topfileNames, String targetFileName)
        {
            String baseroot = seekCommonPath(topfileNames);
            if (baseroot == null)
                return -1;
            List<String> fileNames = convertToFileList(topfileNames);
            //copy each ..to target, by base.
            compressfiles_internal(fileNames, baseroot, targetFileName);


            return 0;
        }
        protected int compressFile_Internal(String fileName, String targetFileName)
        {
            String fn = Path.GetFileName(fileName);
            using (ZipOutputStream s = new ZipOutputStream(File.Create(targetFileName)))
            {
                s.SetLevel(5);
                byte[] buffer = new byte[8192];
                ZipEntry entry = new  ZipEntry(fn);
                entry.DateTime = DateTime.Now;
                s.PutNextEntry(entry);
                using (FileStream fs = File.OpenRead(fileName))
                {
                    int sourceBytes;
                    do
                    {
                        sourceBytes = fs.Read(buffer, 0, 8192);
                        s.Write(buffer, 0, sourceBytes);
                    } while (sourceBytes > 0);
                }
                s.Finish();
                s.Close();
            }
            return 0;
        }
        protected void writeFileToStream_internal(String fileName,ZipOutputStream s,byte[] buffer)
        {
             using (FileStream fs = File.OpenRead(fileName))
                        {
                            int sourceBytes;
                            do
                            {
                                sourceBytes = fs.Read(buffer, 0,
                                buffer.Length);

                               s.Write(buffer, 0, sourceBytes);
 
                            } while (sourceBytes > 0);
                        }
        }
        protected int compressfiles_internal(List<String> fileNames,String basepath, String targetFileName)
        {
            //取得target ..
            String tempFold = getTempFold();
            foreach (String f in fileNames)
                moveFileTo(f, basepath, tempFold);

            //開始compress
            int offset = tempFold.Length;
            if (tempFold.EndsWith("\\") == false)
            {
                offset += 1;
            }
            using (ZipOutputStream s = new ZipOutputStream(File.Create(targetFileName)))
            {
                s.SetLevel(5);
                byte[] buffer = new byte[8192];
 
                try
                {
                    string[] files = Directory.GetFiles(tempFold, "*.*", SearchOption.AllDirectories);
                    foreach (String f in files)
                    {
                        String entryName = f.Substring(offset);

                        ZipEntry entry = new  ZipEntry(entryName);
                        entry.DateTime = DateTime.Now;
                        s.PutNextEntry(entry);
                        writeFileToStream_internal(f, s, buffer);
                    }
                }
                catch (Exception e)
                {
                }
                finally
                {
                    s.Finish();
                    s.Close();
                }
            }
            return 0;
        }

        protected int compressFolder_Internal(String fileName, String targetFileName)
        {
            DirectoryInfo di = new DirectoryInfo(fileName);
            String basedir;
            if (di.Parent == null)
            {
                basedir=di.FullName;
            }
            else
            {
                basedir=di.Parent.FullName;
            }
            List<String> fileNames = new List<String>();
            string[] files = Directory.GetFiles(fileName, "*.*", SearchOption.AllDirectories);
            foreach (String f in files)
            {
                fileNames.Add(f);
            }
            fileNames.Sort();
            return compressfiles_internal(fileNames, basedir, targetFileName);
        }
        public int compressFile(String fileName, String targetFileName)
        {
            if (File.Exists(fileName))
            {
                return compressFile_Internal(fileName, targetFileName);
            }
            if(Directory.Exists(fileName))
            {
                return compressFolder_Internal(fileName,targetFileName);
            }
            return -1;
        }

        public int decompressFile(String fileName, String targetFolder)
        {
            if (Directory.Exists(targetFolder) == false)
            {
                Directory.CreateDirectory(targetFolder);
            }
            if (targetFolder.EndsWith("\\") == false)
                targetFolder += "\\";

            using (FileStream fileStreamIn = new FileStream(fileName, FileMode.Open, FileAccess.Read))
            {
                using (ZipInputStream zipInStream = new ZipInputStream(fileStreamIn))
                {
                    byte[] data = new byte[8192];
                    ZipEntry entry;
                    while ((entry = zipInStream.GetNextEntry()) != null)
                    {
                        // Unzip file
                        String subpath = Path.GetDirectoryName(entry.Name);
                        String zfileName = Path.GetFileName(entry.Name);
                        String folderName = targetFolder + subpath;

                        Directory.CreateDirectory(folderName);
                        if (folderName.EndsWith("\\") == false)
                            zfileName = folderName + "\\" + zfileName;
                        else
                            zfileName = folderName + zfileName;
                        using (FileStream streamWriter = File.Create(zfileName))
                        {
                            int size = 0;
                            while (true)
                            {
                                size = zipInStream.Read(data, 0, data.Length);
                                if (size > 0)
                                {
                                    streamWriter.Write(data, 0, size);
                                }
                                else
                                    break;
                            }
                            streamWriter.Close();
                        }
                    }
                    zipInStream.Close();
                }
                fileStreamIn.Close();
            }
            return 0;
        }
   
    }
}
